/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

#include <gtest/gtest.h>

#include <string.h>

#include "x_list.h"

class TestList : public ::testing::Test
{
protected:
    void SetUp() {};

    void TearDown() {};
};

TEST_F(TestList, DLIST_INIT)
{
    dlist_t n = DLIST_INIT(n);
    EXPECT_EQ(n.prev, n.next);
}

TEST_F(TestList, DLIST_DEF)
{
    DLIST_DEF(n);
    EXPECT_EQ(n.prev, n.next);
}

TEST_F(TestList, dlist)
{
    dlist_t head;

    // dlist_init
    dlist_init(&head);

    EXPECT_EQ(head.prev, head.next);
    EXPECT_EQ(dlist_empty(&head), true);

    typedef struct s {
        dlist_t list;
        char a;
    } s_t;

    s_t s1, s2;

    // dlist_add
    dlist_add(&head, &s1.list);
    dlist_add(&head, &s2.list);

    EXPECT_EQ(head.next, &s2.list);
    EXPECT_EQ(head.prev, &s1.list);
    EXPECT_EQ(dlist_empty(&head), false);

    EXPECT_EQ((s1.list).next, &head);
    EXPECT_EQ((s1.list).prev, &s2.list);
    EXPECT_EQ((s2.list).next, &s1.list);
    EXPECT_EQ((s2.list).prev, &head);

    // dlist_del
    dlist_del(&s2.list);

    EXPECT_EQ(head.next, &s1.list);
    EXPECT_EQ(head.prev, &s1.list);

    EXPECT_EQ((s1.list).next, &head);
    EXPECT_EQ((s1.list).prev, &head);
    EXPECT_EQ((s2.list).next, &s1.list);
    EXPECT_EQ((s2.list).prev, &head);

    // dlist_del_init
    dlist_del_init(&s1.list);

    EXPECT_EQ(head.prev, head.next);
    EXPECT_EQ(dlist_empty(&head), true);

    EXPECT_EQ((s1.list).next, (s1.list).prev);
    EXPECT_EQ((s2.list).next, &s1.list);
    EXPECT_EQ((s2.list).prev, &head);

    // dlist_add_tail
    dlist_add_tail(&head, &s1.list);
    dlist_add_tail(&head, &s2.list);

    EXPECT_EQ(head.next, &s1.list);
    EXPECT_EQ(head.prev, &s2.list);
}

typedef struct {
    dlist_t list;
    char name[16];
} animal;

TEST_F(TestList, dlist_foreach)
{
    animal dog = {0};
    strcpy(dog.name, "dog");
    animal cat = {0};
    strcpy(cat.name, "cat");
    animal pony = {0};
    strcpy(pony.name, "pony");

    DLIST_DEF(head);

    dlist_add(&head, &dog.list);
    dlist_add(&head, &cat.list);
    dlist_add(&head, &pony.list);

    // dlist_foreach
    animal *item = NULL;
    dlist_t *pos = NULL;
    int index = 0;
    dlist_foreach(pos, &head)
    {
        animal *ani = container_of(pos, animal, list);
        if (strcmp(ani->name, "pony") == 0) {
            item = ani;
            break;
        }
        ++index;
    }
    EXPECT_EQ(item, &pony);
    EXPECT_EQ(index, 0);

    // dlist_foreach_backward
    item = NULL;
    pos = NULL;
    index = 0;
    dlist_foreach_backward(pos, &head)
    {
        animal *ani = container_of(pos, animal, list);
        if (strcmp(ani->name, "pony") == 0) {
            item = ani;
            break;
        }
        ++index;
    }
    EXPECT_EQ(item, &pony);
    EXPECT_EQ(index, 2);
}

TEST_F(TestList, dlist_foreach_safe)
{
    animal a1 = {0};
    strcpy(a1.name, "aa");
    animal a2 = {0};
    strcpy(a2.name, "bb");
    animal a3 = {0};
    strcpy(a3.name, "a");
    animal a4 = {0};
    strcpy(a4.name, "bb");

    DLIST_DEF(head);

    dlist_t *pos = NULL;
    dlist_t *n = NULL;
    int cnt = 0;
    char buf[20] = {0};

    // bb a bb aa
    dlist_add(&head, &a1.list);
    dlist_add(&head, &a2.list);
    dlist_add(&head, &a3.list);
    dlist_add(&head, &a4.list);

    pos = NULL;
    memset(buf, 0, sizeof(buf));
    dlist_foreach(pos, &head)
    {
        animal *ani = container_of(pos, animal, list);
        strcat(buf, ani->name);
        strcat(buf, ",");
    }
    EXPECT_EQ(strcmp(buf, "bb,a,bb,aa,"), 0);

    // dlist_foreach_safe
    pos = NULL;
    n = NULL;
    cnt = 0;
    // a aa
    dlist_foreach_safe(pos, n, &head)
    {
        animal *ani = container_of(pos, animal, list);
        if (strcmp(ani->name, "bb") == 0) {
            dlist_del(pos);
            ++cnt;
        }
    }
    EXPECT_EQ(cnt, 2);

    pos = NULL;
    memset(buf, 0, sizeof(buf));
    dlist_foreach(pos, &head)
    {
        animal *ani = container_of(pos, animal, list);
        strcat(buf, ani->name);
        strcat(buf, ",");
    }
    EXPECT_EQ(strcmp(buf, "a,aa,"), 0);

    pos = NULL;
    n = NULL;
    // a bb aa
    dlist_foreach_safe(pos, n, &head)
    {
        animal *ani = container_of(pos, animal, list);
        if (strcmp(ani->name, "a") == 0) {
            dlist_add(pos, &a2.list);
        }
    }

    pos = NULL;
    memset(buf, 0, sizeof(buf));
    dlist_foreach(pos, &head)
    {
        animal *ani = container_of(pos, animal, list);
        strcat(buf, ani->name);
        strcat(buf, ",");
    }
    EXPECT_EQ(strcmp(buf, "a,bb,aa,"), 0);

    pos = NULL;
    n = NULL;
    // bb a bb aa
    dlist_foreach_safe_backward(pos, n, &head)
    {
        animal *ani = container_of(pos, animal, list);
        if (strcmp(ani->name, "a") == 0) {
            dlist_add_tail(pos, &a4.list);
        }
    }

    pos = NULL;
    memset(buf, 0, sizeof(buf));
    dlist_foreach(pos, &head)
    {
        animal *ani = container_of(pos, animal, list);
        strcat(buf, ani->name);
        strcat(buf, ",");
    }
    EXPECT_EQ(strcmp(buf, "bb,a,bb,aa,"), 0);
}

TEST_F(TestList, dlist_foreach_entry)
{
    animal a1 = {0};
    strcpy(a1.name, "aa");
    animal a2 = {0};
    strcpy(a2.name, "bb");
    animal a3 = {0};
    strcpy(a3.name, "a");

    DLIST_DEF(head);

    // a bb aa
    dlist_add(&head, &a1.list);
    dlist_add(&head, &a2.list);
    dlist_add(&head, &a3.list);

    int index = 0;
    animal *ani = NULL;
    animal *item = NULL;
    dlist_foreach_entry(item, &head, animal, list)
    {
        if (strcmp(item->name, "aa") == 0) {
            ani = item;
            break;
        }
        ++index;
    }
    EXPECT_EQ(ani, &a1);
    EXPECT_EQ(index, 2);

    item = NULL;
    animal *next_item = NULL;
    // a bb
    dlist_foreach_entry_safe(item, next_item, &head, animal, list)
    {
        if (strcmp(item->name, "aa") == 0) {
            dlist_del(&item->list);
        }
    }

    item = NULL;
    char buf[20] = {0};
    dlist_foreach_entry(item, &head, animal, list)
    {
        strcat(buf, item->name);
        strcat(buf, ",");
    }
    EXPECT_EQ(strcmp(buf, "a,bb,"), 0);
}
